﻿/**
 * @file audio_aac.cpp
 * Copyright (c) Gaaagaa. All rights reserved.
 * 
 * @author  : Gaaagaa
 * @date    : 2020-02-20
 * @version : 1.0.0.0
 * @brief   : 封装 FDK-AAC 的 API，实现 PCM-S16 音频数据的 AAC 编解码操作。
 */

#include "audio_aac.h"

#include <fdk-aac/aacenc_lib.h>
#include <fdk-aac/aacdecoder_lib.h>

#include <string.h>
#include <errno.h>

////////////////////////////////////////////////////////////////////////////////

#define XEHANDLE(xhandle)  ((HANDLE_AACENCODER)(xhandle))
#define XDHANDLE(xhandle)  ((HANDLE_AACDECODER)(xhandle))

/**********************************************************/
/**
 * @brief 按通道数获取编码使用的通道模式。
 */
static inline CHANNEL_MODE aac_channel_mode(x_uint16_t xut_channels)
{
    CHANNEL_MODE xcmode = MODE_1;

	switch (xut_channels)
    {
	case 1 : xcmode = MODE_1      ; break;
	case 2 : xcmode = MODE_2      ; break;
	case 3 : xcmode = MODE_1_2    ; break;
	case 4 : xcmode = MODE_1_2_1  ; break;
	case 5 : xcmode = MODE_1_2_2  ; break;
	case 6 : xcmode = MODE_1_2_2_1; break;

	default:
        break;
	}

    return xcmode;
}

////////////////////////////////////////////////////////////////////////////////
// aac_encoder_t

//====================================================================

// 
// aac_encoder_t : constructor/destructor
// 

aac_encoder_t::aac_encoder_t(void)
    : m_xht_encoder(X_NULL)
    , m_xut_framelen(0)
{

}

aac_encoder_t::~aac_encoder_t(void)
{
    close();
}

//====================================================================

// 
// aac_encoder_t : public interfaces
// 

/**********************************************************/
/**
 * @brief 打开 AAC 编码器对象。
 * @note
 * 1. xut_bitrate :
 *   1.1 若为 0 时，则默认使用可变位速 XAAC_ENC_VBRMODE_1 模式；
 *   1.2 若 xut_enconfig 指定了 XAAC_ENC_VBRMODE，该参数被忽略。
 * 2. xut_enconfig : 使用 xaac_enc_config_t 中的枚举值进行组合。
 * 
 * @param [in ] xut_channels : 音频通道数。
 * @param [in ] xut_sampbits : 每个音频采样比特数。
 * @param [in ] xut_samprate : 音频采样率。
 * @param [in ] xut_enconfig : 编码操作使用的配置参数。
 * @param [in ] xut_bitrate  : 编码输出的位速。
 * 
 * @return x_errno_t : 错误码。
 * @retval X_ERR_OK, 操作成功。
 * @retval XERR_HINO(xerr_no)，参看 xerrno_table_t 相关枚举值。
 * @retval XERR_LONO(xerr_no)，所引用的模块返回的错误码值。
 */
x_errno_t aac_encoder_t::open(
                x_uint16_t xut_channels,
                x_uint32_t xut_samprate,
                x_uint32_t xut_enconfig /* = XAAC_ENC_DEFAULT */,
                x_uint32_t xut_bitrate  /* = 0 */)
{
    x_errno_t xerr_no = X_ERR_UNKNOW;
    x_int32_t xit_err = X_ERR_UNKNOW;

    HANDLE_AACENCODER xenc_handle = X_NULL;
    AACENC_InfoStruct xenc_info   = { 0 };

    do
    {
        //======================================

        if ((xut_channels <= 0) || (xut_samprate <= 0))
        {
            xerr_no = XERR_MAKE(X_ERR_BAD_ARGS, 0);
            break;
        }

        if (is_open())
        {
            close();
        }

        //======================================
        // open encoder

        xit_err = aacEncOpen(&xenc_handle, 0, xut_channels);
        if (AACENC_OK != xit_err)
        {
            xerr_no = XERR_MAKE(X_ERR_ENCODER_CREATE, xit_err);
            break;
        }

        m_xht_encoder = (x_handle_t)xenc_handle;

        //======================================
        // AOT config

        xit_err = aacEncoder_SetParam(
                            xenc_handle,
                            AACENC_AOT,
                            XAAC_ENC_GET_AOT(xut_enconfig));
        if (AACENC_OK != xit_err)
        {
            xerr_no = XERR_MAKE(X_ERR_SETPARAM_AOT, xit_err);
            break;
        }

        if ((XAAC_ENC_AOT_AAC_ELD == XAAC_ENC_GET_AOT(xut_enconfig)) &&
            XAAC_ENC_BITMODE(xut_enconfig, XAAC_ENC_ELDSBR))
        {
            xit_err = aacEncoder_SetParam(xenc_handle, AACENC_SBR_MODE, 1);
            if (AACENC_OK != xit_err)
            {
                xerr_no = XERR_MAKE(X_ERR_SETPARAM_SBR, xit_err);
                break;
            }
        }

        //======================================
        // set channel mode

        xit_err = aacEncoder_SetParam(
                            xenc_handle,
                            AACENC_CHANNELMODE,
                            aac_channel_mode(xut_channels));
        if (AACENC_OK != xit_err)
        {
            xerr_no = XERR_MAKE(X_ERR_SETPARAM_CHANNEL, xit_err);
            break;
        }

        xit_err = aacEncoder_SetParam(
                            xenc_handle,
                            AACENC_CHANNELORDER,
                            XAAC_ENC_BITMODE(xut_enconfig, XAAC_ENC_CHANNELORDER));
        if (AACENC_OK != xit_err)
        {
            xerr_no = XERR_MAKE(X_ERR_SETPARAM_CHNLORDER, xit_err);
            break;
        }

        //======================================
        // set the sample rate

        xit_err = aacEncoder_SetParam(
                            xenc_handle,
                            AACENC_SAMPLERATE,
                            xut_samprate);
        if (AACENC_OK != xit_err)
        {
            xerr_no = XERR_MAKE(X_ERR_SETPARAM_SAMPRATE, xit_err);
            break;
        }

        //======================================
        // set the transmux

        xit_err = aacEncoder_SetParam(
                            xenc_handle,
                            AACENC_TRANSMUX,
                            XAAC_ENC_GETTMUX(xut_enconfig));
        if (AACENC_OK != xit_err)
        {
            xerr_no = XERR_MAKE(X_ERR_SETPARAM_TRANSMUX, xit_err);
            break;
        }

        //======================================
        // set the afterburner mode

        xit_err = aacEncoder_SetParam(
                            xenc_handle,
                            AACENC_AFTERBURNER,
                            XAAC_ENC_BITMODE(xut_enconfig, XAAC_ENC_AFTERBURNER));
        if (AACENC_OK != xit_err)
        {
            xerr_no = XERR_MAKE(X_ERR_SETPARAM_AFTERBURNER, xit_err);
            break;
        }

        //======================================
        // set the bitrate

        if (0 != XAAC_ENC_GET_VBR(xut_enconfig))
        {
            xit_err = aacEncoder_SetParam(
                            xenc_handle,
                            AACENC_BITRATEMODE,
                            XAAC_ENC_GET_VBR(xut_enconfig));
            if (AACENC_OK != xit_err)
            {
                xerr_no = XERR_MAKE(X_ERR_SETPARAM_BITRATEMODE, xit_err);
                break;
            }
        }
        else if (0 == xut_bitrate)
        {
            xit_err = aacEncoder_SetParam(
                            xenc_handle,
                            AACENC_BITRATEMODE,
                            XAAC_ENC_GET_VBR(XAAC_ENC_VBRMODE_1));
            if (AACENC_OK != xit_err)
            {
                xerr_no = XERR_MAKE(X_ERR_SETPARAM_VBRMODE_1, xit_err);
                break;
            }
        }
        else
        {
            xit_err = aacEncoder_SetParam(
                            xenc_handle,
                            AACENC_BITRATE,
                            xut_bitrate);
            if (AACENC_OK != xit_err)
            {
                xerr_no = XERR_MAKE(X_ERR_SETPARAM_BITRATE, xit_err);
                break;
            }
        }

        //======================================
        // initialize the encoder

        xit_err = aacEncEncode(xenc_handle, X_NULL, X_NULL, X_NULL, X_NULL);
        if (AACENC_OK != xit_err)
        {
            xerr_no = XERR_MAKE(X_ERR_ENCODE_FIRSTINIT, xit_err);
            break;
        }

        //======================================
        // get the encoder information

        xit_err = aacEncInfo(xenc_handle, &xenc_info);
        if (AACENC_OK != xit_err)
        {
            xerr_no = XERR_MAKE(X_ERR_GET_ENCODER_INFO, xit_err);
            break;
        }

        m_xut_framelen =
            xenc_info.inputChannels * xenc_info.frameLength * sizeof(INT_PCM);

        //======================================
        xerr_no = X_ERR_OK;
    } while (0);

    if (XERR_FAILED(xerr_no))
    {
        close();
    }

    return xerr_no;
}

/**********************************************************/
/**
 * @brief 关闭 AAC 编码器对象。
 */
x_void_t aac_encoder_t::close(void)
{
    if (X_NULL != m_xht_encoder)
    {
        HANDLE_AACENCODER xenc_handle = XEHANDLE(m_xht_encoder);
        aacEncClose(&xenc_handle);
        m_xht_encoder = X_NULL;
    }

    m_xut_framelen = 0;
}

/**********************************************************/
/**
 * @brief 返回当前编码器操作的通道数。
 */
x_uint16_t aac_encoder_t::channels(void) const
{
    if (!is_open())
    {
        return 0;
    }

    x_uint32_t xut_channels = 0;

    switch(aacEncoder_GetParam(XEHANDLE(m_xht_encoder), AACENC_CHANNELMODE))
    {
	case MODE_1       : xut_channels = 1; break;
	case MODE_2       : xut_channels = 2; break;
	case MODE_1_2     : xut_channels = 3; break;
	case MODE_1_2_1   : xut_channels = 4; break;
	case MODE_1_2_2   : xut_channels = 5; break;
	case MODE_1_2_2_1 : xut_channels = 6; break;

	default:
        break;
    }

    return (x_uint16_t)xut_channels;
}

/**********************************************************/
/**
 * @brief 返回当前编码器使用的编码采样率。
 */
x_uint32_t aac_encoder_t::sample_rate(void) const
{
    if (!is_open())
    {
        return 0;
    }

    return aacEncoder_GetParam(XEHANDLE(m_xht_encoder), AACENC_SAMPLERATE);
}

/**********************************************************/
/**
 * @brief 执行 PCM-S16 音频数据编码操作。
 * @note 
 * 输入的音频帧 xbuf_pcm 缓存，分为如下两种情况：
 *   1. (X_NULL != xbuf_pcm)，数据字节数为 frame_len()，
 *      不足时，可用 0 填充。
 *   2. (X_NULL == xbuf_pcm)，数据字节数为 0 。
 * 
 * @param [out   ] xbuf_aac : 输出编码后的音频数据所使用的缓存。
 * @param [in,out] xut_size : 入参，xbuf_aac 缓存大小；
 *                            回参，输出编码后的有效数据大小。
 * @param [in    ] xbuf_pcm : 输入的 PCM-S16 音频帧数据。
 * 
 * @return x_errno_t : 错误码。
 * @retval X_ERR_OK, 操作成功。
 * @retval XERR_HINO(xerr_no)，参看 xerrno_table_t 相关枚举值。
 * @retval XERR_LONO(xerr_no)，所引用的模块返回的错误码值。
 */
x_errno_t aac_encoder_t::encode(
        xaacbuf_t xbuf_aac, x_uint32_t & xut_size, cpcmbuf_t xbuf_pcm)
{
    x_errno_t xerr_no = X_ERR_UNKNOW;
    x_int32_t xit_err = X_ERR_UNKNOW;

    do
    {
        //======================================

        if (!is_open())
        {
            xerr_no = XERR_MAKE(X_ERR_ENCODE_UNOPEN, 0);
            break;
        }

        if ((X_NULL == xbuf_aac) || (xut_size <= 0))
        {
            xerr_no = XERR_MAKE(X_ERR_ENCODE_BADARGS, 0);
            break;
        }

        //======================================
        // input args

        AACENC_InArgs input_args =
        {
            /* .numInSamples = */ -1,
            /* .numAncBytes  = */ 0
        };
        if (X_NULL != xbuf_pcm)
        {
            input_args.numAncBytes  = (x_int32_t)frame_len();
            input_args.numInSamples = (x_int32_t)(
                        input_args.numAncBytes / sizeof(INT_PCM));
        }

        x_int32_t  input_ids[] = { (x_int32_t)IN_AUDIO_DATA   };
        x_void_t * input_buf[] = { (x_void_t *)xbuf_pcm       };
        x_int32_t  input_bsz[] = { input_args.numAncBytes     };
        x_int32_t  input_esz[] = { (x_int32_t)sizeof(INT_PCM) };

        AACENC_BufDesc input_desc =
        {
            /* .numBufs           = */ 1,
            /* .bufs              = */ input_buf,
            /* .bufferIdentifiers = */ input_ids,
            /* .bufSizes          = */ input_bsz,
            /* .bufElSizes        = */ input_esz
        };

        //======================================
        // output args

        AACENC_OutArgs output_args =
        {
            /* .numOutBytes  = */ 0,
            /* .numInSamples = */ 0,
            /* .numAncBytes  = */ 0,
            /* .bitResState  = */ 0
        };

        x_int32_t  output_ids[] = { (x_int32_t)OUT_BITSTREAM_DATA };
        x_void_t * output_buf[] = { (x_void_t *)xbuf_aac          };
        x_int32_t  output_bsz[] = { (x_int32_t)xut_size           };
        x_int32_t  output_esz[] = { (x_int32_t)sizeof(x_byte_t)   };

        AACENC_BufDesc output_desc =
        {
            /* .numBufs           = */ 1,
            /* .bufs              = */ output_buf,
            /* .bufferIdentifiers = */ output_ids,
            /* .bufSizes          = */ output_bsz,
            /* .bufElSizes        = */ output_esz
        };

        //======================================
        // encode operation

        xit_err = aacEncEncode(
                        XEHANDLE(m_xht_encoder),
                        &input_desc,
                        &output_desc,
                        &input_args,
                        &output_args);
        if (AACENC_OK != xit_err)
        {
            xerr_no = XERR_MAKE(X_ERR_ENCODE_FAILED, xit_err);
            break;
        }

        xut_size = output_args.numOutBytes;

        //======================================
        xerr_no = X_ERR_OK;
    } while (0);

    if (0 != xit_err)
    {
        xut_size = 0;
    }

    return xerr_no;
}

////////////////////////////////////////////////////////////////////////////////
// aac_decoder_t

//====================================================================

// 
// aac_decoder_t : constructor/destructor
// 

aac_decoder_t::aac_decoder_t(void)
    : m_xht_decoder(X_NULL)
{

}

aac_decoder_t::~aac_decoder_t(void)
{
    close();
}

//====================================================================

// 
// aac_decoder_t : public interfaces
// 

/**********************************************************/
/**
 * @brief 打开解码器对象。
 * 
 * @param [in ] xit_tmux : 解码操作的 AAC 音频数据类型（参看 xaac_dec_tmux_t）。
 * @param [in ] xct_conf : 解码 RAW-AAC 格式时，使用的带外数据。
 * @param [in ] xut_size : xct_conf 中各个缓存的有效数据大小。
 * 
 * @return x_errno_t : 错误码。
 * @retval X_ERR_OK, 操作成功。
 * @retval XERR_HINO(xerr_no)，参看 xerrno_table_t 相关枚举值。
 * @retval XERR_LONO(xerr_no)，所引用的模块返回的错误码值。
 */
x_errno_t aac_decoder_t::open(
            x_int32_t   xit_tmux /* = XAAC_DEC_DEFAULT */,
            x_uchar_t * xct_conf[] /* = X_NULL */,
            x_uint32_t  xut_size[] /* = X_NULL */)
{
    x_errno_t xerr_no = X_ERR_UNKNOW;
    x_int32_t xit_err = X_ERR_UNKNOW;

    HANDLE_AACDECODER xdec_handle = X_NULL;

    do
    {
        //======================================

        if (is_open())
        {
            close();
        }

        switch (xit_tmux)
        {
        case XAAC_DEC_TMUXMP4_RAW  : xit_err = X_ERR_OK; break;
        case XAAC_DEC_TMUXMP4_ADIF : xit_err = X_ERR_OK; break;
        case XAAC_DEC_TMUXMP4_ADTS : xit_err = X_ERR_OK; break;
        case XAAC_DEC_TMUXMP4_MCP1 : xit_err = X_ERR_OK; break;
        case XAAC_DEC_TMUXMP4_MCP0 : xit_err = X_ERR_OK; break;
        case XAAC_DEC_TMUXMP4_LOAS : xit_err = X_ERR_OK; break;
        case XAAC_DEC_TMUXMP4_DRM  : xit_err = X_ERR_OK; break;

        default: xit_err = X_ERR_UNKNOW; break;
        }

        if (X_ERR_OK != xit_err)
        {
            xerr_no = XERR_MAKE(X_ERR_TMUX_UNKNOW, 0);
            break;
        }

        //======================================

        xdec_handle = aacDecoder_Open((TRANSPORT_TYPE)xit_tmux, 1);
        if (X_NULL == xdec_handle)
        {
            xerr_no = XERR_MAKE(X_ERR_CREATE_DECODER, 0);
            break;
        }

        if ((X_NULL != xct_conf) && (X_NULL != xut_size))
        {
            xit_err = aacDecoder_ConfigRaw(xdec_handle, xct_conf, xut_size);
            if (AAC_DEC_OK != xit_err)
            {
                xerr_no = XERR_MAKE(X_ERR_CONFRAW_FAILED, xit_err);
                break;
            }
        }

        //======================================

        m_xht_decoder = (x_handle_t)xdec_handle;
        xdec_handle   = X_NULL;

        //======================================
        xerr_no = X_ERR_OK;
    } while (0);

    if (X_NULL != xdec_handle)
    {
        aacDecoder_Close(xdec_handle);
        xdec_handle = X_NULL;
    }

    return xerr_no;
}

/**********************************************************/
/**
 * @brief 关闭解码器对象。
 */
x_void_t aac_decoder_t::close(void)
{
    if (X_NULL != m_xht_decoder)
    {
        aacDecoder_Close(XDHANDLE(m_xht_decoder));
        m_xht_decoder = X_NULL;
    }
}

/**********************************************************/
/**
 * @brief 返回当前解码的数据中 音频的编码对象类型（返回 0 时，表示未知）。
 * @note 参看 xaac_aot_t 相关枚举值。
 */
x_int32_t aac_decoder_t::aot(void) const
{
    CStreamInfo * xstr_info =
        aacDecoder_GetStreamInfo(XDHANDLE(m_xht_decoder));
    if (X_NULL == xstr_info)
    {
        return 0;
    }

    return (x_int32_t)xstr_info->aot;
}

/**********************************************************/
/**
 * @brief 返回当前解码的数据中 音频的通道数（返回 0 时，表示未知）。
 */
x_uint16_t aac_decoder_t::channels(void) const
{
    CStreamInfo * xstr_info =
        aacDecoder_GetStreamInfo(XDHANDLE(m_xht_decoder));
    if (X_NULL == xstr_info)
    {
        return 0;
    }

    return (x_uint16_t)xstr_info->numChannels;
}

/**********************************************************/
/**
 * @brief 返回当前解码的数据中 音频的采样率（返回 0 时，表示未知）。
 */
x_uint32_t aac_decoder_t::sample_rate(void) const
{
    CStreamInfo * xstr_info =
        aacDecoder_GetStreamInfo(XDHANDLE(m_xht_decoder));
    if (X_NULL == xstr_info)
    {
        return 0;
    }

    return (x_uint32_t)xstr_info->sampleRate;
}

/**********************************************************/
/**
 * @brief 返回当前解码的每帧音频数据长度（返回 0 时，表示未知）。
 */
x_uint32_t aac_decoder_t::frame_len(void) const
{
    CStreamInfo * xstr_info =
        aacDecoder_GetStreamInfo(XDHANDLE(m_xht_decoder));
    if (X_NULL == xstr_info)
    {
        return 0;
    }

    return (x_uint32_t)(xstr_info->frameSize   *
                        xstr_info->numChannels *
                        sizeof(INT_PCM));
}

/**********************************************************/
/**
 * @brief 清理解码器缓存。
 */
x_void_t aac_decoder_t::cleanup(void)
{
    aacDecoder_SetParam(XDHANDLE(m_xht_decoder), AAC_TPDEC_CLEAR_BUFFER, 1);
}

/**********************************************************/
/**
 * @brief 执行解码操作。
 * 
 * @param [out   ] xbuf_pcm : 输出解码后的 PCM-S16 音频数据所使用的缓存。
 * @param [in,out] xut_plen : 入参，xbuf_pcm 缓存大小（按字节计数）；
 *                            回参，xbuf_pcm 中有效数据长度（按字节计数）。
 * @param [in    ] xbuf_aac : 输入待解码的 AAC 音频数据的缓存。
 * @param [in,out] xut_alen : 入参，xbuf_aac 缓存中有效数据长度（按字节计数）；
 *                            回参，xbuf_aac 剩余的（未输入到解码器缓存的）字节数。
 * @param [in    ] xut_flag : 解码操作的 组合标识 值（参看 xaac_dec_flag_t）。
 * 
 * @return x_errno_t : 错误码。
 * @retval X_ERR_OK, 操作成功。
 * @retval XERR_HINO(xerr_no)，参看 xerrno_table_t 相关枚举值。
 * @retval XERR_LONO(xerr_no)，所引用的模块返回的错误码值。
 */
x_errno_t aac_decoder_t::decode(
                xpcmbuf_t    xbuf_pcm,
                x_uint32_t & xut_plen,
                caacbuf_t    xbuf_aac,
                x_uint32_t & xut_alen,
                x_uint32_t   xut_flag /* = 0 */)
{
    x_errno_t xerr_no = X_ERR_UNKNOW;
    x_int32_t xit_err = X_ERR_UNKNOW;

    do
    {
        //======================================

        if (!is_open())
        {
            xerr_no = XERR_MAKE(X_ERR_DECODE_UNOPEN, 0);
            break;
        }

        if ((X_NULL == xbuf_pcm) || (xut_plen < sizeof(INT_PCM)))
        {
            xerr_no = XERR_MAKE(X_ERR_DECODE_BADARGS, 0);
            break;
        }

        //======================================

        if ((X_NULL != xbuf_aac) && (xut_alen > 0))
        {
            xaacbuf_t  xbuf_input[1] = { const_cast< xaacbuf_t >(xbuf_aac) };
            x_uint32_t xlen_input[1] = { xut_alen };

            xit_err = aacDecoder_Fill(
                            XDHANDLE(m_xht_decoder),
                            xbuf_input,
                            xlen_input,
                            &xut_alen);
            if (AAC_DEC_OK != xit_err)
            {
                xerr_no = XERR_MAKE(X_ERR_DECODE_FILLAAC, xit_err);
                break;
            }
        }

        //======================================

        xit_err = aacDecoder_DecodeFrame(
                        XDHANDLE(m_xht_decoder),
                        xbuf_pcm,
                        xut_plen / sizeof(INT_PCM),
                        xut_flag);
        if (!IS_OUTPUT_VALID(xit_err))
        {
            xerr_no = XERR_MAKE(X_ERR_DECODE_FAILED, xit_err);

            xut_plen = 0;
            break;
        }

        xut_plen = frame_len();

        //======================================
        xerr_no = XERR_MAKE(X_ERR_OK, xit_err);
    } while (0);

    return xerr_no;
}
